#pragma once

#include <amp.h>
#include <amp_math.h>
using namespace concurrency;
using namespace concurrency::fast_math;

class Model
{
public:
    //virtual FLOAT generatePath(FLOAT random, FLOAT time) const =0;
};
class BlackScholes : public Model
{
public:
    static float cnd_calc(float d)
    {
        const float a1 = 0.31938153f, a2 = -0.356563782f, a3 = 1.781477937f, a4 = -1.821255978f, a5 = 1.330274429f;
        const float isqrt2pi = 0.39894228040143267793994605993438f;
        float x = 1.0f / (1.0f + 0.2316419f * fast_math::fabsf(d));
    
        float cnd = isqrt2pi * fast_math::expf(- 0.5f * d * d) * (x * (a1 + x * (a2 + x * (a3 + x * (a4 + x * a5)))));
        return (d > 0) ? 1.0f - cnd : cnd;
    }

    static float closedForm(float S, float X, float T, float R, float V)
    {
        float sqrtT = sqrt(T);
        float d1 = (log(S / X) + (R + 0.5f * V * V) * T) / (V * sqrtT);
        float d2 = d1 - V * sqrtT;
    
        float cndd1 = cnd_calc(d1);
        float cndd2 = cnd_calc(d2);
    
        float exp_RT = exp(- R * T);
        return S * cndd1 - X * exp_RT * cndd2;
        //out_put_result[i]  = X * exp_RT * (1.0f - cndd2) - S * (1.0f - cndd1);
    }
    
    BlackScholes(FLOAT spot, FLOAT vol, FLOAT rate) 
        : _spot(spot), _vol(vol), _rate(rate) 
    {}

    FLOAT spot() const restrict (cpu, amp) { return _spot; }
    FLOAT vol() const restrict (cpu, amp) { return _vol; }
    FLOAT rate() const restrict (cpu, amp) { return _rate; }

    FLOAT drift(FLOAT time) const restrict (cpu, amp) 
    { return (_rate - 0.5f * _vol * _vol) * time; }
    
    FLOAT df(FLOAT time) const restrict (cpu, amp) 
    { return exp(-_rate * time); }
    
    FLOAT generatePath(FLOAT random, FLOAT time) const restrict (cpu, amp)
    { 
        return _spot * exp(drift(time) + random * _vol * time); 
    }
private:
    FLOAT _spot, _vol, _rate;
};

